#include <string>
#include <iostream>
#include <fstream>
#include <assert.h>
#include <boost/tokenizer.hpp>
#include <DagLoader.h>
#include <Types.h>
#include <Defines.h>
#include <MallocAlign.h>
#ifdef __HUGE_PAGES__
#include <sys/shm.h>
extern int shmidNodes;
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void * HugePageAlloc( size_t numBytes, int & shmid ) {
   void * p;
   U64 numPages = numBytes / ( 2*1024*1024 );
   numPages++;
   numBytes = numPages * 2*1024*1024;
   shmidNodes = shmget( IPC_PRIVATE, numBytes, SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W );
   if( shmidNodes == -1 ) {
      perror("shmget failed: ");
      exit(1);
   }
   p = shmat( shmidNodes, NULL, 0 );
   if( p == (void *)(-1) ) {
      perror("shmat failed: ");
      exit(1);
   }
   return p;
}
#endif
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
using namespace std;
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void ExtractRawFileName( const std::string & fileName, std::string & rawFileName ) {

   const std::string fext = "qts";
   
   std::string t1 = "";
   std::string t2 = "";
   
   boost::char_separator<char> sep("./");
   boost::tokenizer<boost::char_separator<char> > tokens(fileName, sep);
   for( boost::tokenizer<boost::char_separator<char> >::iterator it = tokens.begin(); it != tokens.end(); ++it ) {
      DEBUG_PRINT( "token = " << *it );
      // keep two most recent tokens in t1 & t2
      t2 = t1;
      t1 = *it;
      // if the most recent token is "mtx" then the raw file name is found
      if( t1 == fext ) {
         rawFileName = t2;
         return;
      }
   }
   rawFileName = "rawFileNameParseError";
   return;
}
bool ReadDagMtxProperties( FILE * IFH, const std::string & fileName, MatrixProperties & mtxProps ) {

   // read 1st 6 entries in dag file: m, n, nnz, nnzHgt, rcaHgt, numNodes
   // parse file name into raw file name
   // put all the metadata into mtxProps data struct
   
   U64 m;
   U64 n;
   U64 nnz;
   U64 numNodes;
   U64 numDags;

   #ifdef __MAC_OSX__
   fscanf( IFH, "%llu", &m   );
   fscanf( IFH, "%llu", &n   );
   fscanf( IFH, "%llu", &nnz );
   fscanf( IFH, "%llu", &numNodes );
   fscanf( IFH, "%llu", &numDags  );
   #else
   fscanf( IFH, "%lu",  &m   );
   fscanf( IFH, "%lu",  &n   );
   fscanf( IFH, "%lu",  &nnz );
   fscanf( IFH, "%lu",  &numNodes );
   fscanf( IFH, "%lu",  &numDags  );
   #endif
      
   mtxProps.m   = m;
   mtxProps.n   = n;
   mtxProps.nnz = nnz;
   mtxProps.numNodes = numNodes;
   mtxProps.numDags  = numDags;
   
   for( U32 dagIdx = 0; dagIdx < numDags; dagIdx++ ) {
      U64 rootNodeIdx;
      U64 dagHgt;
      
      #ifdef __MAC_OSX__
      fscanf( IFH, "%llu", &rootNodeIdx );
      fscanf( IFH, "%llu", &dagHgt      );
      #else
      fscanf( IFH, "%lu",  &rootNodeIdx );
      fscanf( IFH, "%lu",  &dagHgt      );
      #endif
      
      mtxProps.rootNodeIdxs.push_back( rootNodeIdx );
      mtxProps.dagHgts.push_back( dagHgt );
   }
   
   mtxProps.fullFileName = fileName;
   
   return true;
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
U64 * ReadNodesFromFile( const MatrixProperties & mtxProps, FILE * IFH, const U32 deg ) {

   DEBUG_PRINT( "... ReadNodesFromFile()" );

   U64 node;
   U64 *nodes = NULL;
   U64 numBytes;
   U64 numWordsRead = 0ULL;
   const U64 numNodes = mtxProps.numNodes;
   const U64 numWordsPerNode = deg >> 1ULL;
   const U64 numWords = numNodes * numWordsPerNode;

   numBytes = sizeof(U64) * numNodes * numWordsPerNode;
   
   #ifdef __HUGE_PAGES__
   nodes = (U64 *) HugePageAlloc( numBytes, shmidNodes );
   #else
   nodes = (U64*) MALLOC( numBytes );
   #endif
   
   assert( nodes );
      
   #ifdef __VERBOSE__
   const U64 kbytes = numBytes >> 10;
   const U64 Mbytes = kbytes   >> 10;
   printf( "////////////////////////////////////////////////////////////////////////////////\n" );
   printf( "////////////////////////////////////////////////////////////////////////////////\n" );
   printf( "dag information:\n" );
   #ifdef __MAC_OSX__
   printf( "       m = %llu\n", mtxProps.m   );
   printf( "       n = %llu\n", mtxProps.n   );
   printf( "     nnz = %llu\n", mtxProps.nnz );
   printf( "  degree = %d\n",        deg     );
   printf( "numNodes = %llu\n", numNodes     );
   printf( "numBytes = %llu\n", numBytes     );
   printf( "  kBytes = %llu\n",   kbytes     );
   printf( "  MBytes = %llu\n",   Mbytes     );
   #else    
   printf( "       m = %lu\n",  mtxProps.m   );
   printf( "       n = %lu\n",  mtxProps.n   );
   printf( "     nnz = %lu\n",  mtxProps.nnz );
   printf( "  degree = %d\n",        deg     );
   printf( "numNodes = %lu\n",  numNodes     );
   printf( "numBytes = %lu\n",  numBytes     );
   printf( "  kBytes = %lu\n",    kbytes     );
   printf( "  MBytes = %lu\n",    Mbytes     );
   #endif
   printf( "////////////////////////////////////////////////////////////////////////////////\n" );
   printf( "////////////////////////////////////////////////////////////////////////////////\n" );
   #endif // __VERBOSE__
   
   DEBUG_PRINT( "Reading " << numNodes << " nodes from file." );
   DEBUG_PRINT( "Reading " << numWords << " words from file." );
   
   // -- TODO -- fixup EOF checking
   const U32 bufsize = 4*1024;
   char buf[ bufsize ];
   
   do {      
      fgets( buf, bufsize, IFH );
   } while( strcmp( buf, "# begin nodes\n" ) );
   
   while( numWordsRead < numWords ) {
      
      #ifdef __MAC_OSX__
      fscanf( IFH, "%016llx", &node );
      #else
      fscanf( IFH, "%016lx",  &node );
      #endif
      nodes[numWordsRead] = node;
      
      numWordsRead++;
   }
   
   fclose( IFH );
   assert( numWordsRead == numWords );
   
   DEBUG_PRINT( "... leaving ReadNodesFromFile()" );
   return nodes;
}
U64 * LoadDagMtx( const string & dagMtxFile, const U32 deg, MatrixProperties & mtxProps ) {

   DEBUG_PRINT( "... entering LoadDagMtx()" );
   
   #ifdef __SPMV__
   printf( "Loading matrix from ascii dag file: %s\n", dagMtxFile.c_str() );
   #endif
   
   FILE * IFH;
   IFH = fopen( dagMtxFile.c_str(), "r" );
   assert( IFH != NULL );
   
   DEBUG_PRINT( "Calling ReadDagMtxProperties();" );
   assert( ReadDagMtxProperties( IFH, dagMtxFile, mtxProps ) );
   
   DEBUG_PRINT( "Found the following using ReadMtxProperties()" );
   DEBUG_PRINT( "  m = " << mtxProps.m   );
   DEBUG_PRINT( "  n = " << mtxProps.n   );
   DEBUG_PRINT( "nnz = " << mtxProps.nnz );
   DEBUG_PRINT( "numNodes = " << mtxProps.numNodes );
   DEBUG_PRINT( "fullFileName = " << mtxProps.fullFileName );
   DEBUG_PRINT( "rawFileName  = " << mtxProps.rawFileName  );
   
   return ReadNodesFromFile( mtxProps, IFH, deg );
}
